package com.oragraph.gui;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.HashSet;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import com.oragraph.data.Edge;
import com.oragraph.data.Node;
import com.oragraph.data.Properties;
import com.oragraph.data.PropertiesChangeListener;
import com.oragraph.data.Tab;

import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.util.Relaxer;
import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.GraphMouseListener;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.layout.LayoutTransition;
import edu.uci.ics.jung.visualization.subLayout.TreeCollapser;
import edu.uci.ics.jung.visualization.util.Animator;

public class TabPanel extends JPanel implements ItemListener, PropertiesChangeListener {

	private Tab _tab;
	
	private DefaultModalGraphMouse _graphMouse;
	private VisualizationViewer<Node,Edge> _visualizationServer;
	private Layout<Node, Edge> _layout;
	private JSplitPane _rightPane;
	
	private PropertiesPanel _mutablePropertiesPanel, _immutablePropertiesPanel;
	
	public TabPanel(Tab tab) {
		super();
		_tab=tab;
		_tab.getRoot().getImmutableProperties().addChangeListener(this);
		_tab.getRoot().getMutableProperties().addChangeListener(this);
		
		// PROPERTIES PANEL ------------------------------------------------------------
		
		_mutablePropertiesPanel=new PropertiesPanel(null, true);
		JScrollPane mutableProperties=new JScrollPane(
				_mutablePropertiesPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		mutableProperties.setBorder(BorderFactory.createTitledBorder("Mutable properties"));

		_immutablePropertiesPanel=new PropertiesPanel(null, true);
		JScrollPane immutableProperties=new JScrollPane(
				_immutablePropertiesPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
				JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		immutableProperties.setBorder(BorderFactory.createTitledBorder("Immutable properties"));
		
		_rightPane=new JSplitPane(JSplitPane.VERTICAL_SPLIT, 
				mutableProperties, immutableProperties);
		
		
		// EXPAND-COLLAPSE --------------------------------------------------------------
		JButton expandLayerButton=new JButton("ExpandLayer");
		expandLayerButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                expandLayer();
            }
        });	

		JButton collapseButton=new JButton("Collapse");
		collapseButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                collapse();
            }
        });	
		
		JButton expandButton=new JButton("Expand");
		expandButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                expand();
            }
        });	
		
		JButton editFilterButton=new JButton("Edit filter");
		editFilterButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                editFilter();
            }
        });	

		JPanel expandCollapsePanel=new JPanel();
		expandCollapsePanel.add(expandLayerButton);
		expandCollapsePanel.add(collapseButton);
		expandCollapsePanel.add(expandButton);
		expandCollapsePanel.add(editFilterButton);
		
		// GRAPH --------------------------------------------------------------
		
		_layout=_createLayout();
		
		_visualizationServer=new VisualizationViewer<Node,Edge>(_layout);
		_visualizationServer.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
		_visualizationServer.getPickedVertexState().addItemListener(this);
		
		_graphMouse = new DefaultModalGraphMouse();
		_graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
		_visualizationServer.setGraphMouse(_graphMouse);
		
		_visualizationServer.addKeyListener(_graphMouse.getModeKeyListener());
		
        _visualizationServer.addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent arg0) {
				super.componentResized(arg0);
				_layout.setSize(arg0.getComponent().getSize());
			}});
		
        GraphZoomScrollPane graphPanel = new GraphZoomScrollPane(_visualizationServer);
		
		// MAIN SWING ---------------------------------------------------------
		JPanel mainPanel=new JPanel();
		mainPanel.setLayout(new BorderLayout());
		mainPanel.add(graphPanel, BorderLayout.CENTER);
		mainPanel.add(expandCollapsePanel, BorderLayout.SOUTH);
		
		JSplitPane mainSplit=new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mainPanel, _rightPane);
		mainSplit.setDividerLocation(0.75d);
		
		this.setLayout(new BorderLayout());
		this.add(mainSplit, BorderLayout.CENTER);
	}
	
	public void expandLayer() {
		Collection picked = new HashSet(_visualizationServer.getPickedVertexState().getPicked());
		if(picked.size()==1) {
			Object[] selected=picked.toArray();
			if (selected[0] instanceof Node) {
				Node node=(Node)(selected[0]);
				Vector<Node> children=node.getChildren();
				
				if (children.size()==0) return;
				
				for (int i=0; i<children.size(); i++) {
					Node childNode=children.elementAt(i);
					childNode.getImmutableProperties().addChangeListener(this);
					childNode.getMutableProperties().addChangeListener(this);
					_tab.graph.addVertex(childNode);
					_tab.graph.addEdge(new Edge(), node, childNode);
				}
				
				Layout<Node, Edge> newLayout=_createLayout();
				
        		LayoutTransition<Node,Edge> lt = 
					new LayoutTransition<Node,Edge>(_visualizationServer, 
							_visualizationServer.getGraphLayout(), newLayout);
				
				Animator animator = new Animator(lt);
				animator.start();

				_visualizationServer.repaint();				
			}
		}
	}
	
	private Layout<Node, Edge> _createLayout() {
		return new FRLayout(_tab.graph);
	}
	
	public void expand() {
		Collection picked = new HashSet(_visualizationServer.getPickedVertexState().getPicked());
		if(picked.size()==1) {
			Object[] selected=picked.toArray();
			if (selected[0] instanceof Forest) {
				Forest selectedForest=(Forest)selected[0];
				TreeCollapser collapser=new TreeCollapser();

                try {
					collapser.expand(_tab.graph, selectedForest);
				} 
                catch (Exception e) {
					e.printStackTrace();
				}

                _visualizationServer.getPickedVertexState().clear();
                _visualizationServer.repaint();				
			}
		}
		
	}
	
	public void collapse() {
		Collection picked = new HashSet(_visualizationServer.getPickedVertexState().getPicked());
		if(picked.size()==1) {
			Object[] selected=picked.toArray();
			if (selected[0] instanceof Node) {
				Node selectedNode=(Node)selected[0];
				TreeCollapser collapser=new TreeCollapser();

                try {
					collapser.collapse(_visualizationServer.getGraphLayout(), 
							_tab.graph, selectedNode);
				} 
                catch (Exception e) {
					e.printStackTrace();
				}

                _visualizationServer.getPickedVertexState().clear();
                _visualizationServer.repaint();				
			}
		}
	}

	public void editFilter() {
		Collection picked = new HashSet(_visualizationServer.getPickedVertexState().getPicked());
		if(picked.size()==1) {
			Object[] selected=picked.toArray();
			if (selected[0] instanceof Node) {
				Node selectedNode=(Node)selected[0];
				EditFilterDialog efd=new EditFilterDialog(selectedNode);
				efd.setVisible(true);
			}
		}
	}

	@Override
	public void itemStateChanged(ItemEvent arg0) {
		Collection picked = new HashSet(_visualizationServer.getPickedVertexState().getPicked());
		if(picked.size()==1) {
			Object[] selected=picked.toArray();
			if (selected[0] instanceof Node) {
				Node selectedNode=(Node)selected[0];
				_mutablePropertiesPanel.setProperties(selectedNode.getMutableProperties());
				_immutablePropertiesPanel.setProperties(selectedNode.getImmutableProperties());
				_rightPane.setDividerLocation(0.5d);
				return;
			}
		}
		
		// clear properties panels
		_mutablePropertiesPanel.setProperties(null);
		_immutablePropertiesPanel.setProperties(null);
		_rightPane.setDividerLocation(0.5d);
		
	}

	@Override
	public void propertyAdded(Properties properties, String key) {
		// TODO
	}

	@Override
	public void propertyChanged(Properties properties, String key) {
		if (key.equals("name")) {
			_visualizationServer.repaint();
		}
		else if (key.equals("filter")) {
			Object propertiesOwner=properties.getOwner();
			if ((propertiesOwner!=null) && (propertiesOwner instanceof Node)) {
				Node node=(Node)propertiesOwner;
				Vector<Node> children=node.getChildren();
				node.reset();
				for (int i=0; i<children.size(); i++) {
					_tab.graph.removeVertex(children.elementAt(i));
				}
				
				Layout<Node, Edge> newLayout=_createLayout();
				
        		LayoutTransition<Node,Edge> lt = 
					new LayoutTransition<Node,Edge>(_visualizationServer, 
							_visualizationServer.getGraphLayout(), newLayout);
				
				Animator animator = new Animator(lt);
				animator.start();

				_visualizationServer.repaint();				
				
			}
		}
	}

}
